#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<string>
#include<cstring>
#include<unistd.h>
#include<sys/types.h>
#include<sys/wait.h>
using namespace std;
const int basesize = 1024;//用户输入指令行输入的最多字符个数
const int gargvnum = 64;//命令行参数数目
const int envnum = 64;//环境变量数目
int lastcode = 0;//定义全局的退出码

char* gargv[gargvnum];//全局命令行参数表

char* genv[envnum];//本地的环境变量表

int gargc = 0;//全局命令行字符个数(以空格为界)

char pwd[basesize];//全局变量，用于存放shell的实时工作路径
char pwdenv[basesize]={0};//用putenv()，来更新环境变量pwd，
void InitEnv()
{
    extern char** environ;//获得系统的环境变量
    int pos = 0;
    while(environ[pos])
    {
        int len = strlen(environ[pos]);
        genv[pos]=(char*)malloc(len+1);//本地的环境变量表；多开出的一位用于存'\0'
        strncpy(genv[pos],environ[pos],len);//用系统的环境变量初始化本地环境变量，模拟shell读入配置信息过程
        genv[pos][len]=0;//用于存'\0'
        pos++;
    }
    genv[pos]=nullptr;   
}
void AddEnv(const char* item )
{
    int pos = 0;
    while(genv[pos])
    {
        pos++;
    }
    genv[pos]=(char*)malloc(strlen(item)+1);//命令行中的字符串是临时的，所以要深拷贝
    strncpy(genv[pos],item,strlen(item)+1);//'\0'也要加进来
    genv[++pos]=nullptr;
}
string GetUserName()
{
    char* tmp = getenv("USER");
    string name = tmp == NULL ? "None" : tmp;
     return name;
}
string GetHostName()
{
    char* tmp = getenv("hostname");
    string name = tmp == NULL ? "None" : tmp;
     return name;
}
string GetPath()//
{
    if(NULL==getcwd(pwd,sizeof(pwd)))//调用系统接口获取当前实时工作路径，写入全局变量中
    {
        return "None";
    }
    //导环境变量
    snprintf(pwdenv,sizeof(pwdenv),"PWD=%s",pwd);
    putenv(pwdenv);
    return pwd;
}
string MakeCommandLine()//制作命令行格式
{
    char Line[basesize];
    snprintf(Line,basesize,"[%s@%s %s]#",GetUserName().c_str(),GetHostName().c_str(),GetPath().c_str());
    return Line;
}
void PrintCommandLine()//打印命令行
{
    printf("%s",MakeCommandLine().c_str());
    fflush(stdout);
}                
bool GetCommandLine(char* command,int size)//获取命令行参数
{
    char* result = fgets(command,size,stdin);//从标准输入流文件中读数据，载入到命令行参数数组中
    if(result == NULL)
    {
        return false;
    }
    command[strlen(command)-1]=0;//获取纯净的字符串，每次输入命令后回车，会使最后一个字符是\n，将最后一个字符改为'\0'结束
    return true;
}
void  PraseCommandLine(char* command)//3、处理命令,将command_line里的数据以空格为界限处理成单个字符存入全局变量gargv中
{
    memset(gargv,0,sizeof(gargv));//先将原先的全局命令行参数重置为0；防止上次的数据对这次造成影响
    gargc=0;
    const char* token = " ";
    gargv[gargc++]=strtok(command,token);
    while(gargv[gargc++]=strtok(NULL,token));
    gargc--;//最后读取NULL时还多加了一次；
}
//有些命令由子进程执行
//有些必须要由shell本身执行（内建命令）
bool ExecuteCommand()//4、执行命令
{
    pid_t id = fork();
    if(id<0)
        return false;

    if(id == 0)//创建子进程操作
    {
        execvpe(gargv[0],gargv,genv);//调用这个系统接口可以使子进程继承自己的环境变量
        exit(1);
    }
    int status = 0;
    pid_t rid = waitpid(id,&status,0);//阻塞式等待
    if(rid>0)
    {
        if(WIFEXITED(status))//进程正常退出；后八位对应信号
        {
            lastcode = WEXITSTATUS(status);//获得进程退出码；前八位对应退出码
        }
        else
        {
            lastcode = -1;
        }
        return true;
    }
    return false;
}   
//shell自己执行命令
bool CheckAndExecuteBuilt()
{
    if(strcmp(gargv[0],"cd")==0)
    {
        if(gargc==2)
        {
            chdir(gargv[1]);//系统调用，改变shell进程自己的路径
            lastcode = 0;
        }
        else
            lastcode = 1;//最后执行的退出码异常
        return true;
    }
    else if(strcmp(gargv[0],"export")==0)
    {
        if(gargc==2)
        {
            AddEnv(gargv[1]);
            lastcode = 0;
        }
        else
            lastcode = 2;
        return true;
    }
    else if(strcmp(gargv[0],"env")==0)
    {
        for(int i = 0;genv[i];++i)
            printf("%s\n",genv[i]);
        lastcode = 0;
        return true;
    }
    else if(strcmp(gargv[0],"echo")==0)
    {
        if(gargc==2)
        {
            if(gargv[1][0]=='$')
            {
                if(gargv[1][1]=='?')
                {
                    printf("%d\n",lastcode);
                    lastcode=0;
                }
            }
            else
            {
                printf("%s\n",gargv[1]);
            }
        }
        else
            lastcode = 3;
        return true;
    }
    return false;
}
int main()
{
    InitEnv();//维护本地环境变量（模拟读取配置信息过程）
    char command_line[basesize];//命令行
    while(true)
    {
        //InitEnv();//维护本地环境变量（模拟读取配置信息过程）,因为要校准环境变量表中的pwd所以每次都要读一遍配置信息
        PrintCommandLine();//1、打印命令行
        if(!GetCommandLine(command_line,basesize))//2、获取命令行参数 
        {
            continue;//没有获取信息
        } 
        //printf("%s\n",command_line);测试2
        PraseCommandLine(command_line);//3、处理命令
        //printf("%d\n",gargc);测试3
        //for(int i = 0;gargv[i];++i)
        //{
        //    printf("gargv[%d]:%s\n",i,gargv[i]);
        //}
        if(CheckAndExecuteBuilt())//判断是否为内建命令
        {
            continue;
        }
        ExecuteCommand();//4、执行命令 
    }
    return 0;
}
